﻿using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace In66.Net.Share.Registrar.WebRegistrar
{
    public static partial class ServiceRegistrar
    {
        /// <summary>
        /// AddNSwagger
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IServiceCollection AddNSwagger(this IServiceCollection services, IServiceInfo ServiceInfo)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            services.AddApiVersioning(option =>
            {
                // 可选，为true时API返回支持的版本信息
                option.ReportApiVersions = false;
                // 不提供版本时，默认为1.0
                option.AssumeDefaultVersionWhenUnspecified = true;
                // 请求中未指定版本时默认为1.0
                option.DefaultApiVersion = new ApiVersion(1, 0);
                //option.ApiVersionReader = new HeaderApiVersionReader("apiVersion");
                option.ApiVersionReader = new QueryStringApiVersionReader("apiVersion");

            }).AddVersionedApiExplorer(option =>
            {
                // 版本名的格式：v+版本号
                option.GroupNameFormat = "'v'V";
                //属性是为了标记当客户端没有指定版本号的时候是否使用默认版本号
                option.AssumeDefaultVersionWhenUnspecified = true;
            });

            //获取webapi版本信息，用于swagger多版本支持 
            var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();


            //遍历版本信息给不同版本添加NSwag支持，如果只写一个就只有一份
            foreach (var description in provider.ApiVersionDescriptions)
            {
                //添加NSwag
                services.AddOpenApiDocument(settings =>
                {
                    settings.DocumentName = description.GroupName;
                    settings.Version = description.GroupName;
                    settings.Title = ServiceInfo.ServiceName;
                    settings.Description = ServiceInfo.Description;
                    settings.ApiGroupNames = new string[] { description.GroupName };
                    settings.UseControllerSummaryAsTagDescription = true;

                    settings.AddSecurity("身份认证Token", Enumerable.Empty<string>(), new NSwag.OpenApiSecurityScheme()
                    {
                        Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}（注意两者之间是一个空格）",
                        Name = "Authorization",
                        In = NSwag.OpenApiSecurityApiKeyLocation.Header,
                        Type = NSwag.OpenApiSecuritySchemeType.ApiKey
                    });
                });
            }
            return services;
        }

        /// <summary>
        /// AddSwagger
        /// </summary>
        /// <param name="services"></param>
        /// <param name="ServiceInfo"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IServiceCollection AddSwagger(this IServiceCollection services, IServiceInfo ServiceInfo)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            services.AddApiVersioning(option =>
            {
                option.ReportApiVersions = true;
                option.AssumeDefaultVersionWhenUnspecified = true;
                option.DefaultApiVersion = new ApiVersion(1, 0);
                option.ApiVersionSelector = new CurrentImplementationApiVersionSelector(option);

                //Header api-version
                //options.ApiVersionReader = new HeaderApiVersionReader("api-version");

                //Query api-version
                option.ApiVersionReader = new QueryStringApiVersionReader("api-version");

            }).AddVersionedApiExplorer(option =>
            {
                // 版本名的格式：v+版本号
                option.GroupNameFormat = "'v'V";
                //属性是为了标记当客户端没有指定版本号的时候是否使用默认版本号
                option.AssumeDefaultVersionWhenUnspecified = true;
            });

            services.AddTransient<IConfigureOptions<SwaggerGenOptions>, SwaggerConfigureOptions>();

            services.AddSwaggerGen(options =>
            {
                options.OperationFilter<OperationFilter>();
                options.SchemaFilter<SchemaFilter>();
                options.DocumentFilter<DocumentFilter>();
                //options.OperationFilter<XCodeSamplesOperationFilter>();

                options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetEntryAssembly().GetName().Name}.xml"), true);

                #region 校验

                options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}（注意两者之间是一个空格）",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                    Scheme = "Bearer"
                });

                options.AddSecurityRequirement(new OpenApiSecurityRequirement()
                {
                  {
                    new OpenApiSecurityScheme
                    {
                      Reference = new OpenApiReference
                      {
                        Type = ReferenceType.SecurityScheme,
                        Id = "Bearer"
                      },
                      Scheme = "oauth2",
                      Name = "Bearer",
                      In = ParameterLocation.Header,
                    },new List<string>()
                  }
                });

                #endregion
            });

            return services;
        }

        #region SetSpanEditable

        /// <summary>
        /// Swagger设置Span为可编辑
        /// </summary>
        /// <param name="swaggerUIOptions"></param>
        public static void SetSpanEditable(this Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIOptions swaggerUIOptions)
        {
            StringBuilder stringBuilder = new StringBuilder(swaggerUIOptions.HeadContent);
            stringBuilder.Append(@"
                    <script type='text/javascript'>
                    window.addEventListener('load', function () {
                        setTimeout(() => {
                            let createElement = window.ui.React.createElement
                            ui.React.createElement = function () {
                                let array = Array.from(arguments)
                                if (array.length == 3) {
                                    if (array[0] == 'span' && !array[1]) {
                                        array[1] = { contentEditable: true }
                                    }
                                }

                                let ele = createElement(...array)
                                return ele
                            }
                        })
                    })
                    </script>");
            swaggerUIOptions.HeadContent = stringBuilder.ToString();
        }

        /// <summary>
        /// NSwagger设置Span为可编辑
        /// </summary>
        /// <param name="swaggerUIOptions"></param>
        public static void SetSpanEditable(this NSwag.AspNetCore.SwaggerUi3Settings swaggerUIOptions)
        {
            StringBuilder stringBuilder = new StringBuilder(swaggerUIOptions.CustomHeadContent);
            stringBuilder.Append(@"
                    <script type='text/javascript'>
                    window.addEventListener('load', function () {
                        setTimeout(() => {
                            let createElement = window.ui.React.createElement
                            ui.React.createElement = function () {
                                let array = Array.from(arguments)
                                if (array.length == 3) {
                                    if (array[0] == 'span' && !array[1]) {
                                        array[1] = { contentEditable: true }
                                    }
                                }

                                let ele = createElement(...array)
                                return ele
                            }
                        })
                    })
                    </script>");
            swaggerUIOptions.CustomHeadContent = stringBuilder.ToString();
        }
        #endregion

        #region Filter
        /// <summary>
        ///OperationFilterFilter
        /// </summary>
        public class OperationFilter : IOperationFilter
        {
            /// <summary>
            /// Apply
            /// </summary>
            /// <param name="operation"></param>
            /// <param name="context"></param>
            public void Apply(OpenApiOperation operation, OperationFilterContext context)
            {
                var apiDescription = context.ApiDescription;

                //判断接口遗弃状态，对接口进行标记调整
                operation.Deprecated |= apiDescription.IsDeprecated();

                if (operation.Parameters == null)
                {
                    return;
                }

                //api-version 参数添加必填验证
                foreach (var parameter in operation.Parameters)
                {
                    var description = apiDescription.ParameterDescriptions.First(p => p.Name == parameter.Name);

                    if (parameter.Description == null)
                    {
                        parameter.Description = description.ModelMetadata?.Description;
                    }

                    if (parameter.Schema.Default == null && description.DefaultValue != null)
                    {
                        parameter.Schema.Default = new OpenApiString(description.DefaultValue.ToString());
                    }

                    parameter.Required |= description.IsRequired;
                }
            }
        }

        /// <summary>
        /// DocumentFilter
        /// </summary>
        public class DocumentFilter : IDocumentFilter
        {
            /// <summary>
            /// Apply
            /// </summary>
            /// <param name="swaggerDoc"></param>
            /// <param name="context"></param>
            /// <exception cref="NotImplementedException"></exception>
            public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
            {
            }
        }

        /// <summary>
        /// SchemaFilter
        /// </summary>
        public class SchemaFilter : ISchemaFilter
        {
            /// <summary>
            /// Apply
            /// </summary>
            /// <param name="schema"></param>
            /// <param name="context"></param>
            public void Apply(OpenApiSchema schema, SchemaFilterContext context)
            {
                if (!context.Type.IsEnum)
                {
                    return;
                }

                schema.Enum.Clear();
                foreach (var name in Enum.GetNames(context.Type))
                {
                    Enum e = (Enum)Enum.Parse(context.Type, name);

                    schema.Enum.Add(new OpenApiString($"{e:D}={GetDescription(e)}"));
                }
            }

            /// <summary>
            /// GetDescription
            /// </summary>
            /// <param name="value"></param>
            /// <returns></returns>
            private static string GetDescription(Enum value)
            {
                FieldInfo field = value.GetType().GetField(value.ToString());

                var va = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;

                return va == null ? value.ToString() : va.Description;
            }
        }
        #endregion

        #region Options
        /// <summary>
        /// 配置swagger生成选项
        /// </summary>
        public class SwaggerConfigureOptions : IConfigureOptions<SwaggerGenOptions>
        {
            private readonly IApiVersionDescriptionProvider provider;

            /// <summary>
            /// SwaggerConfigureOptions
            /// </summary>
            /// <param name="provider"></param>
            public SwaggerConfigureOptions(IApiVersionDescriptionProvider provider) => this.provider = provider;

            /// <summary>
            /// Configure
            /// </summary>
            /// <param name="options"></param>
            public void Configure(SwaggerGenOptions options)
            {
                foreach (var description in provider.ApiVersionDescriptions)
                {
                    options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
                }
            }

            /// <summary>
            /// CreateInfoForApiVersion
            /// </summary>
            /// <param name="description"></param>
            /// <returns></returns>
            private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
            {
                var info = new OpenApiInfo()
                {
                    Title = Assembly.GetEntryAssembly()?.GetName().Name,
                    Version = "v" + description.ApiVersion.ToString(),
                    Description = description.GroupName,
                    //Contact = new OpenApiContact
                    //{
                    //    Email = "891367701@qq.com",
                    //    Name = "yanh",
                    //    Url = new Uri("https://github.com")
                    //},
                    //License = new OpenApiLicense
                    //{
                    //    Name = "License",
                    //    Url = new Uri("https://github.com")
                    //}
                };

                if (description.IsDeprecated)
                {
                    info.Description = info.Version + " 版本已弃用尽快升级新版本";
                }

                return info;
            }
        }
        #endregion
    }
}
